package com.talkweb.demo.search;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @description: 描述
 * @className：Test
 * @author: lj
 * @date: 2024/10/8 17:47
 */
public class PdfSearch {
    private final static Logger log = LoggerFactory.getLogger(PdfSearch.class);
    private static String source = null;
    private static Long max = null;
    private static String target = null;
    private static String keyword = null;
    private static String[] keywords = null;
    private static Integer poolSize = null;

    public static void search(Scanner scanner) {
        params(scanner);

        ExecutorService executor = Executors.newFixedThreadPool(poolSize);
        File sourceFile = new File(source);
        File[] files = sourceFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            String sourceFileAbsolutePath = file.getAbsolutePath();
            String fileName = file.getName();
            if (!file.isFile() || !(fileName.endsWith(".pdf") || fileName.endsWith(".doc") || fileName.endsWith(".docx"))) {
                log.info("==== 非.pdf/.doc/.docx文件，不处理:" + sourceFileAbsolutePath + " ====");
                continue;
            }
            if (FileCacheUtil.exists(sourceFileAbsolutePath)) {
                log.info("==== 已处理过的文件，不重复处理:" + sourceFileAbsolutePath + " ====");
                continue;
            }
            long length = file.length();
            if (length > max) {
                log.error("==== 文件大小超过限制，不处理:" + sourceFileAbsolutePath + " size:" + formatSize(length) + " ====");
                continue;
            }
            String msg = "开始处理第[" + (i + 1) + "/" + files.length + "]个文件:" + sourceFileAbsolutePath + " size:" + formatSize(length) + " ====";
            System.out.println("==========================丢入线程池~~~" + (i + 1));
            runThread(executor, file, msg);
        }
        while (!executor.isTerminated()) {
            System.out.println("==========================如果所有任务已完成，请手动结束进程~~~");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void params(Scanner scanner) {
        while (null == source) {
            System.out.print("搜索目录：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                File file = new File(input);
                if (file.exists()) {
                    if (file.isDirectory()) {
                        source = input;
                    } else {
                        System.err.println("！！必须是目录，不能直接指定文件！！");
                    }
                }
            }
        }

        while (null == max) {
            System.out.print("文件最大限制（M）[100]：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if (NumberUtil.isNumber(input)) {
                    max = Long.valueOf(input) * 1024 * 1024;
                }
            } else {
                max = 100L * 1024 * 1024;
            }
        }

        while (null == target) {
            System.out.print("命中后移动到目录：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if (input.equalsIgnoreCase(source)) {
                    System.err.println("！！移动到的目录不能与搜索目录相同！！");
                } else {
                    target = input;
                }
            }
        }

        while (null == keyword) {
            System.out.print("搜索关键字，多个用英文逗号隔开：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                keyword = input;
                keywords = input.split(",");
            }
        }

        while (null == poolSize) {
            System.out.print("并发线程数[4]：");
            String input = scanner.nextLine();
            if (StrUtil.isNotBlank(input)) {
                input = input.trim();
                if (NumberUtil.isNumber(input)) {
                    Integer i = Integer.valueOf(input);
                    if (i > 999) {
                        System.err.println("！！最多999个线程！！");
                    }
                    poolSize = i;
                }
            } else {
                poolSize = 4;
            }
        }

        System.out.println("=======================================================");
        System.out.println("搜索目录：" + source);
        System.out.println("文件最大限制（M）：" + formatSize(max));
        System.out.println("命中后移动到目录：" + target);
        System.out.println("异常时移动到目录：" + target + File.separator + "error");
        System.out.println("搜索关键字：" + keyword);
        System.out.println("并发线程数：" + poolSize);
        System.out.println("=======================================================");

        System.out.print("是否确认开始（y/n）[y]：");
        String input = scanner.nextLine();
        scanner.close();
        if (StrUtil.isNotBlank(input) && !"y".equalsIgnoreCase(input)) {
            System.exit(0);
        }
    }

    private static void runThread(ExecutorService executor, File sourceFile, String msg) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                log.info(msg);
                begin(sourceFile);
            }
        });
    }

    private static void begin(File sourceFile) {
        String sourceFileAbsolutePath = sourceFile.getAbsolutePath();
        String ext = sourceFile.getName();
        boolean exists = false;
        if (ext.endsWith(".docx") || ext.endsWith(".doc")) {
            exists = WordKeywordSearch(sourceFile);
        } else if (ext.endsWith(".pdf")) {
            exists = PDFKeywordSearch(sourceFile);
        } else {
            log.error("不支持的文件类型：" + sourceFileAbsolutePath);
        }
        if (exists) {
            String targetPath = sourceFileAbsolutePath.replace(source, target);
            File targetFile = new File(targetPath);
            if (targetFile.exists()) {
                log.error("目标路径已存在，不做任何操作：" + targetPath);
                return;
            }
            targetFile.getParentFile().mkdirs();
            FileUtil.move(sourceFile, targetFile, false);
        }
        FileCacheUtil.cache(sourceFileAbsolutePath);
    }

    private static boolean WordKeywordSearch(File sourceFile) {
        try (FileInputStream fis = new FileInputStream(sourceFile);
             XWPFDocument document = new XWPFDocument(fis)) {
            StringBuffer sb = new StringBuffer();
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                sb.append(paragraph.getText().trim());
            }
            String content = sb.toString();
            boolean contains = SearchUtil.contains(content, keywords);
            if (contains) {
                log.info(" =================命中：" + sourceFile.getAbsolutePath());
                return true;
            } else {
                log.info("未命中" + sourceFile.getAbsolutePath());
                return false;
            }

        } catch (IOException e) {
            e.printStackTrace();
            File targetFile = new File(target + File.separator + "error" + File.separator + sourceFile.getName());
            FileUtil.move(sourceFile, targetFile, false);
        }
        return false;
    }

    private static boolean PDFKeywordSearch(File sourceFile) {
        try (PDDocument document = PDDocument.load(sourceFile)) {
            PDFTextStripper stripper = new PDFTextStripper();
            String content = stripper.getText(document);
            boolean contains = SearchUtil.contains(content, keywords);
            if (contains) {
                log.info(" =================命中：" + sourceFile.getAbsolutePath());
                return true;
            } else {
                log.info("未命中" + sourceFile.getAbsolutePath());
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            File targetFile = new File(target + File.separator + "error" + File.separator + sourceFile.getName());
            FileUtil.move(sourceFile, targetFile, false);
        }
        return false;
    }

    private static String formatSize(long size) {
        if (size < 1024) {
            return size + "B";
        } else if (size < 1024 * 1024) {
            return size / 1024 + "K";
        } else {
            return size / 1024 / 1024 + "M";
        }
    }
}
